package com.jctrip.hotel.source.service.CNBooking.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jctrip.hotel.domain.dao.HtSourceInfoDao;
import com.jctrip.hotel.domain.entity.HtSourceInfo;
import com.jctrip.hotel.source.model.CnBooking.req.*;
import com.jctrip.hotel.source.model.CnBooking.resp.*;
import com.jctrip.hotel.source.service.HotelCommonTaskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.jctrip.hotel.common.enums.OrderStatusEnum;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.util.BeanUtil;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.common.util.XMLUtil;
import com.jctrip.hotel.source.client.CNBEClient;
import com.jctrip.hotel.source.model.CnBooking.CityDataDTO;
import com.jctrip.hotel.source.model.CnBooking.IdentityInfoDTO;
import com.jctrip.hotel.source.model.CnBooking.req.BaseRequest.IdentityInfo;
import com.jctrip.hotel.source.model.CnBooking.req.BaseRequest.ScrollingInfo;
import com.jctrip.hotel.source.model.CnBooking.req.BookingReq.BookingConditions;
import com.jctrip.hotel.source.model.CnBooking.req.OrderCancelReq.OrderCancelData;
import com.jctrip.hotel.source.model.CnBooking.req.OrderSearchReq.OrderSearchData;
import com.jctrip.hotel.source.model.CnBooking.req.QueryHotelReq.SearchConditions;
import com.jctrip.hotel.source.model.CnBooking.req.QueryRoomStatusReq.StayDateRange;
import com.jctrip.hotel.source.model.CnBooking.req.QueryRoomTypeReq.SearchRoomTypeConditions;
import com.jctrip.hotel.source.model.CnBooking.resp.BookingResp.BookingOrderData;
import com.jctrip.hotel.source.model.CnBooking.resp.OrderSearchResp.QueryOrderData;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryCityResp.Country;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryHotelResp.HotelsData;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Hotel;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Hotels;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.PriceAndStatu;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Rate;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Rates;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Room;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.RoomStatusHotel;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Rooms;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomTypeResp.RoomTypeData;
import com.jctrip.hotel.source.model.OrderDataResult;
import com.jctrip.hotel.source.model.PriceCheckParam;
import com.jctrip.hotel.source.model.wehotel.req.OrderData;
import com.jctrip.hotel.source.service.CNBooking.CNBookingService;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author henry
 * @Date 2023/7/2 14:57
 * @Description
 */
@Service
@Slf4j
public class CNBookingServiceImpl implements CNBookingService {

    @Resource
    private CNBEClient cnbeClient;

    @Resource
    private HtSourceInfoDao htSourceInfoDao;

    @Autowired
    private HotelCommonTaskService hotelCommonTaskService;

    @Override
    public String parseHotelId(String innId) {
        if (StringUtils.isBlank(innId)) {
            return null;
        }
        return innId.substring(SourceEnum.CNBE.getCode().length());
    }

    @Override
    public String parseInnId(String hotelId) {
        if (null == hotelId) {
            return null;
        }
        return SourceEnum.CNBE.getCode() + hotelId;
    }

    @Override
    public String parseAbroadHotelId(String innId) {
        if (StringUtils.isBlank(innId)) {
            return null;
        }
        return innId.substring(SourceEnum.CNBEINT.getCode().length());
    }

    @Override
    public String parseAbroadInnId(String hotelId) {
        if (null == hotelId) {
            return null;
        }
        return SourceEnum.CNBEINT.getCode() + hotelId;
    }


    @Override
    public List<CityDataDTO> getCityData(IdentityInfoDTO infoDTO) {
        try {
            IdentityInfo info = new IdentityInfo();
            BeanUtil.copyProperties(infoDTO, info);
            BaseRequest baseRequest = new BaseRequest();
            baseRequest.setIdentityInfo(info);
            String auth = Base64.getEncoder()
                    .encodeToString((infoDTO.getUserName() + infoDTO.getPassWord() + infoDTO.getSecurityKey()
                            + infoDTO.getSignature()).getBytes("UTF-8"));
            QueryCityResp queryCityResp = cnbeClient.getCity(baseRequest, auth);
            if (queryCityResp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(queryCityResp.getMessageInfo().getDescription());
            }
            List<CityDataDTO> result = new ArrayList<>();
            List<Country> countrys = queryCityResp.getData().getCountrys();
            Optional.ofNullable(countrys).orElse(new ArrayList<>()).forEach(country -> {
                Optional.ofNullable(country.getProvinces()).orElse(new ArrayList<>()).forEach(province -> {
                    Optional.ofNullable(province.getCity()).orElse(new ArrayList<>()).forEach(city -> {
                        if (city == null) {
                            return;
                        }
                        CityDataDTO cityDataDTO = new CityDataDTO();
                        cityDataDTO.setCityName(city.getCityName());
                        cityDataDTO.setCityId(city.getCityId());
                        cityDataDTO.setProvinceId(province.getProvinceId());
                        cityDataDTO.setProvinceName(province.getProvinceName());
                        cityDataDTO.setCountryId(country.getCountryId());
                        cityDataDTO.setCountryName(country.getCountryName());
                        result.add(cityDataDTO);
                    });
                });
            });
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public HotelsData queryHotel(String countryId, String provinceId, String cityId, String lang, Integer current,
                                 Integer pageSize, String hotelId, IdentityInfoDTO infoDTO) {
        QueryHotelReq req = new QueryHotelReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);

        req.setIdentityInfo(info);
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(30).pageNo(current).pageItems(pageSize).build();
        req.setScrollingInfo(scrollingInfo);
        req.setActionName("HotelSearch");
        SearchConditions searchConditions =
                SearchConditions.builder().countryId(countryId).provinceId(provinceId).hotelId(hotelId).cityId(cityId).lang(lang)
                        .build();
        req.setSearchConditions(searchConditions);
        try {
            String respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));
            QueryHotelResp resp = XMLUtil.convertXmlToObject(respData, QueryHotelResp.class);
            if (resp == null) {
                log.error("queryHotel error respData {}", respData);
                throw new RuntimeException("queryHotel error respData error");
            }
            if (resp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(resp.getMessageInfo().getDescription());
            }
            return resp.getData();
        } catch (Exception e) {
            log.error("queryHotel error", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public RoomTypeData queryRoomType(String countryId, String hotelId, String lang, IdentityInfoDTO infoDTO) {
        QueryRoomTypeReq req = new QueryRoomTypeReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);

        req.setIdentityInfo(info);
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(40).build();
        req.setScrollingInfo(scrollingInfo);
        req.setActionName("RoomSearch");
        SearchRoomTypeConditions searchConditions =
                SearchRoomTypeConditions.builder().countryId(countryId).hotelId(hotelId).build();
        req.setSearchConditions(searchConditions);
        try {
            String respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));
            QueryRoomTypeResp resp = XMLUtil.convertXmlToObject(respData, QueryRoomTypeResp.class);
            if (resp == null) {
                log.error("queryRoomType error respData {}", respData);
                throw new RuntimeException("queryRoomType error respData error");
            }
            return resp.getData();
        } catch (Exception e) {
            log.error("queryRoomType error", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public ValiadRoomStatusResp.ValiadRoomStatusInfo valiadRoomstatus(String hotelId, String roomId, String rateplanId, String roomCount,
                                                                      String checkIn, String checkOut, String currency, String orderAmount,
                                                                      IdentityInfoDTO infoDTO) {
        ValiadRoomStatusReq req = new ValiadRoomStatusReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);
        req.setIdentityInfo(info);
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(40).build();
        req.setScrollingInfo(scrollingInfo);
        req.setActionName("PreBookingCheck");
        ValiadRoomStatusReq.BookingConditions searchConditions = ValiadRoomStatusReq.BookingConditions.builder()
                .hotelId(hotelId).roomId(roomId).rateplanId(rateplanId).checkIn(checkIn).roomCount(roomCount).checkOut(checkOut)
                .currency(currency).orderAmount(orderAmount).build();
        req.setSearchConditions(searchConditions);
        try {
            String respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));
            ValiadRoomStatusResp resp = XMLUtil.convertXmlToObject(respData, ValiadRoomStatusResp.class);
            if (resp == null) {
                log.error("PreBookingCheck error respData {}", respData);
                throw new RuntimeException("PreBookingCheck error respData error");
            }
            return resp.getData();
        } catch (Exception e) {
            log.error("PreBookingCheck error", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public RoomStatusHotel queryRoomStatus(String countryId, String currency, String hotelId, String roomId,
                                           String ratePlanId,
                                           String start, String end,
                                           IdentityInfoDTO infoDTO) {
        QueryRoomStatusReq req = new QueryRoomStatusReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);
        req.setIdentityInfo(info);
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(40).build();
        req.setScrollingInfo(scrollingInfo);
        req.setActionName("RatePlanSearch");
        QueryRoomStatusReq.SearchRoomTypeConditions searchConditions =
                QueryRoomStatusReq.SearchRoomTypeConditions.builder()
                        .roomId(roomId)
                        .ratePlanId(ratePlanId)
                        .countryId(countryId)
                        .hotelId(hotelId)
                        .Currency(currency)
                        .stayDateRange(StayDateRange.builder().checkIn(start).checkOut(end).build()).build();
        req.setSearchConditions(searchConditions);
        try {
            String respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));
            QueryRoomStatusResp resp = XMLUtil.convertXmlToObject(respData, QueryRoomStatusResp.class);
            if (resp == null) {
                log.error("queryRoomStatus error respData {}", respData);
                throw new RuntimeException("queryRoomStatus error respData error");
            }
            if (resp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(resp.getMessageInfo().getDescription());
            }
            return resp.getData();
        } catch (Exception e) {
            log.error("queryRoomStatus e", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public BookingOrderData booking(BookingReq req) {
        req.setActionName("Booking");
        req.setScrollingInfo(ScrollingInfo.builder().displayReq(40).build());
        try {
            String respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));
            BookingResp resp = XMLUtil.convertXmlToObject(respData, BookingResp.class);
            if (resp == null) {
                log.error("booking error respData {}", respData);
                throw new RuntimeException("booking error ");
            }

            if (resp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(resp.getMessageInfo().getDescription());
            }
            if (!"31004".equals(resp.getData().getReturnCode())) {
                throw new RuntimeException(resp.getData().getReturnMessage());
            }
            return resp.getData().getOrderData();
        } catch (Exception e) {
            log.error("booking error", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public OrderDataResult postOrder(CNBEPostOrderReq req, IdentityInfoDTO infoDTO, PriceCheckParam priceCheck) {
        if (Objects.isNull(req) || Objects.isNull(infoDTO)) {
            throw new RuntimeException("请求参数错误");
        }
        // 构建查询房态, todo 币种默认CNY
        RoomStatusHotel roomStatusHotel =
                queryRoomStatus(null, req.getCurrency(), req.getHotelId(), req.getRoomId(), req.getRateplanId(),
                        req.getCheckIn(), req.getCheckOut(), infoDTO);
        if (Objects.isNull(roomStatusHotel)) {
            throw new RuntimeException("酒店房态不存在");
        }
        Hotels hotels = roomStatusHotel.getHotels();
        if (Objects.isNull(hotels)) {
            throw new RuntimeException("酒店房态不存在");
        }
        Hotel hotel = hotels.getHotel().stream().filter(query -> query.getHotelId().equals(req.getHotelId())).findAny()
                .orElse(null);
        if (Objects.isNull(hotel)) {
            throw new RuntimeException("酒店房态不存在");
        }
        Rate rate = Optional.ofNullable(hotel.getRooms()).map(Rooms::getRoom)
                .map(rooms -> Optional.ofNullable(rooms).orElse(new ArrayList<>()).stream()
                        .filter(room -> room.getRoomId().equals(req.getRoomId())).findAny().orElse(new Room()))
                .map(Room::getRates).map(Rates::getRate)
                .map(rates -> rates.stream().filter(reteResp -> reteResp.getRatePlanId().equals(req.getRateplanId()))
                        .findAny()
                        .orElse(null)).orElse(null);
        if (Objects.isNull(rate)) {
            throw new RuntimeException("酒店房态不存在");
        }
        List<PriceAndStatu> priceAndStatu = Objects.requireNonNull(rate.getPriceAndStatus()).getPriceAndStatu();
        if (CollectionUtils.isEmpty(priceAndStatu)) {
            throw new RuntimeException("酒店房态不存在");
        }

        AtomicReference<BigDecimal> totalAdvanceRate = new AtomicReference<>(new BigDecimal(0));
        AtomicReference<BigDecimal> raisedTotalPrice = new AtomicReference<>(BigDecimal.valueOf(0));

        AtomicReference<Boolean> valiad = new AtomicReference<>(true);

        priceAndStatu.forEach(priceAndStatus -> {
            if (priceAndStatus.getDate().equals(req.getCheckOut())) {
                return;
            }
            BigDecimal price = new BigDecimal(priceAndStatus.getPrice());
            if ("0".equals(priceAndStatus.getCount()) && !"026001".equals(priceAndStatus.getStatu())) {
                valiad.set(false);
            }
            totalAdvanceRate.set(totalAdvanceRate.get().add(price));
            raisedTotalPrice.set(raisedTotalPrice.get().add(priceCheck.getRaiseFunc().apply(price)));
        });
        if (!valiad.get()) {
            hotelCommonTaskService.insertOrderErrorLog(req.getCustomerOrderId(),
                    JsonUtil.toString(req), "库存不足");
            throw new RuntimeException("库存不足");
        }
        if (priceCheck.getTotalRate()
                .compareTo(totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount()))
                        .subtract(priceCheck.getTolerancePrice())) < 0) {
            String error = String.format("订单销售价不能低于采购总价，订单销售价=%s，采购总价=%s, 容差金额=%s, 目前差额=%s",
                    priceCheck.getTotalRate(),
                    totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount())),
                    priceCheck.getTolerancePrice(),
                    totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount())).subtract(priceCheck.getTotalRate()));
            hotelCommonTaskService.insertOrderErrorLog(req.getCustomerOrderId(),
                    JsonUtil.toString(req), error);
            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}, 目前差额={}",
                    priceCheck.getTotalRate(),
                    totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount())),
                    priceCheck.getTolerancePrice(),
                    totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount())).subtract(priceCheck.getTotalRate()));
            throw new RuntimeException("订单销售价不能低于采购总价");
        }
        // 构建参数
        BookingReq bookingReq = new BookingReq();
        bookingReq.setActionName("Booking");
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);
        bookingReq.setIdentityInfo(info);
        BookingConditions bookingConditions = new BookingConditions();
        BeanUtil.copyProperties(req, bookingConditions);
        BookingReq.BookInfo bookInfo = new BookingReq.BookInfo();
        BeanUtil.copyProperties(req.getBookInfo(), bookInfo);
        List<BookingReq.GuestInfo> guestInfos = new ArrayList<>();
        Optional.ofNullable(req.getGuestInfos()).orElse(new ArrayList<>()).forEach(e -> {
            BookingReq.GuestInfo guestInfo = BookingReq.GuestInfo.builder().build();
            BeanUtil.copyProperties(e, guestInfo);
            guestInfos.add(guestInfo);
        });
        bookingConditions.setGuestInfos(guestInfos);
        bookingConditions.setBookInfo(bookInfo);
        bookingConditions.setOrderAmount(
                totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount())).toString());
        //todo 币种默认CNY
        bookingConditions.setCurrency(req.getCurrency());
        //todo 币种默认CNY
        ValiadRoomStatusResp.ValiadRoomStatusInfo valiadRoomStatusInfo = valiadRoomstatus(req.getHotelId(), req.getRoomId(),
                req.getRateplanId(), req.getRoomCount(), req.getCheckIn(), req.getCheckOut(),
                req.getCurrency(), bookingConditions.getOrderAmount(), infoDTO);
        if (StringUtils.isBlank(valiadRoomStatusInfo.getReturnCode())
                || !"31002".equals(valiadRoomStatusInfo.getReturnCode())) {
            throw new RuntimeException(valiadRoomStatusInfo.getReturnMessage());
        }
        bookingReq.setSearchConditions(bookingConditions);
        BookingOrderData booking = booking(bookingReq);
        Integer orderState = OrderStatusEnum.BOOK_FAILED.getCode();
        if (StringUtils.isNotBlank(booking.getOrderStatusId())) {
            switch (booking.getOrderStatusId()) {
                case "9":
                case "10":
                case "27":
                    orderState = OrderStatusEnum.BOOK_SUCCESS.getCode();
                    break;
                case "28":
                    orderState = OrderStatusEnum.ARRIVED.getCode();
                    break;
                case "29":
                    orderState = OrderStatusEnum.QUITED.getCode();
                    break;
                default:
                    orderState = OrderStatusEnum.NOT_CONFIRMED.getCode();
            }
        }
        return OrderDataResult.builder().orderCode(booking.getOrderId()).externalId(req.getCustomerOrderId())
                .totalRate(totalAdvanceRate.get().multiply(new BigDecimal(req.getRoomCount()))).orderState(orderState)
                .requestJsonStr(JsonUtil.toString(req))
                .responseJsonStr(JsonUtil.toString(booking)).build();
    }

    @Override
    public OrderData queryOrder(IdentityInfoDTO infoDTO, String orderId, String outOrderNo) {
        if (StringUtils.isBlank(orderId) && StringUtils.isBlank(outOrderNo)) {
            throw new RuntimeException("cnbe 订单号或者jc 订单编号不能同时为空");
        }
        OrderSearchData orderSearchData =
                OrderSearchData.builder().customerOrderId(outOrderNo).orderId(orderId).build();
        OrderSearchReq req = new OrderSearchReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);
        req.setIdentityInfo(info);
        req.setSearchConditions(orderSearchData);
        req.setActionName("OrderSearch");
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(40).build();
        req.setScrollingInfo(scrollingInfo);
        String respData = null;
        try {
            respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));

            OrderSearchResp resp = XMLUtil.convertXmlToObject(respData, OrderSearchResp.class);
            if (resp == null) {
                log.error("booking error respData {}", respData);
                throw new RuntimeException("booking error ");
            }

            if (resp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(resp.getMessageInfo().getDescription());
            }
            if (CollectionUtils.isEmpty(resp.getData())) {
                throw new RuntimeException("订单不存在");
            }

            OrderData result = new OrderData();
            result.setOrderCode(orderId);
            result.setExternalId(outOrderNo);
            QueryOrderData queryOrderData = resp.getData().stream()
                    .filter(order -> order.getOrderData().getCustomerOrderId().equals(outOrderNo)
                            || order.getOrderData().getOrderId().equals(orderId)).findAny().orElse(null);
            if (queryOrderData == null) {
                throw new RuntimeException("订单不存在");
            }
            result.setOrderCode(queryOrderData.getOrderData().getOrderId());
            Integer orderState = OrderStatusEnum.BOOK_SUCCESS.getCode();
            if (StringUtils.isNotBlank(queryOrderData.getOrderData().getOrderStatesId())) {
                switch (queryOrderData.getOrderData().getOrderStatesId()) {
                    case "8":
                        orderState = OrderStatusEnum.CANCELED.getCode();
                        break;
                    case "9":
                    case "10":
                    case "27":
                        orderState = OrderStatusEnum.BOOK_SUCCESS.getCode();
                        break;
                    case "28":
                        orderState = OrderStatusEnum.ARRIVED.getCode();
                        break;
                    case "29":
                        orderState = OrderStatusEnum.QUITED.getCode();
                        break;
                }
            }
            result.setOrderState(orderState);
            return result;
        } catch (Exception e) {
            log.error("cnbe queryOrder error req {}, res : {}", XMLUtil.convertObjectToXml(req), respData, e);
            throw new RuntimeException("订单查询失败");
        }
    }

    @Override
    public QueryOrderData queryCNBEOrder(String sourceId, String orderId, String outOrderNo) {
        if (StringUtils.isBlank(orderId) && StringUtils.isBlank(outOrderNo)) {
            throw new RuntimeException("cnbe 订单号或者jc 订单编号不能同时为空");
        }
        HtSourceInfo htSourceInfo =
                htSourceInfoDao.selectOne(new LambdaQueryWrapper<HtSourceInfo>().eq(HtSourceInfo::getSourceId,
                        sourceId));
        if (htSourceInfo == null) {
            throw new RuntimeException("供应商账户不存在");
        }
        IdentityInfoDTO infoDTO = IdentityInfoDTO.builder().appId(htSourceInfo.getSourceAppId())
                .securityKey(htSourceInfo.getSourceSecret())
                .userName((String) JsonUtil.toMap(htSourceInfo.getExtra()).get("userName"))
                .passWord((String) JsonUtil.toMap(htSourceInfo.getExtra()).get("passWord"))
                .signature((String) JsonUtil.toMap(htSourceInfo.getExtra()).get("signature"))
                .ratePlanId((String) JsonUtil.toMap(htSourceInfo.getExtra()).get("ratePlanId"))
                .days(15).build();

        OrderSearchData orderSearchData =
                OrderSearchData.builder().customerOrderId(outOrderNo).orderId(orderId).build();
        OrderSearchReq req = new OrderSearchReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);
        req.setIdentityInfo(info);
        req.setSearchConditions(orderSearchData);
        req.setActionName("OrderSearch");
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(40).build();
        req.setScrollingInfo(scrollingInfo);
        String respData = null;
        try {
            respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));

            OrderSearchResp resp = XMLUtil.convertXmlToObject(respData, OrderSearchResp.class);
            if (resp == null) {
                log.error("booking error respData {}", respData);
                throw new RuntimeException("booking error ");
            }

            if (resp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(resp.getMessageInfo().getDescription());
            }
            if (CollectionUtils.isEmpty(resp.getData())) {
                throw new RuntimeException("订单不存在");
            }

            QueryOrderData queryOrderData = resp.getData().stream()
                    .filter(order -> order.getOrderData().getCustomerOrderId().equals(outOrderNo)
                            || order.getOrderData().getOrderId().equals(orderId)).findAny().orElse(null);
            if (queryOrderData == null) {
                throw new RuntimeException("订单不存在");
            }

            return queryOrderData;
        } catch (Exception e) {
            log.error("cnbe queryOrder error req {}, res : {}", XMLUtil.convertObjectToXml(req), respData, e);
            throw new RuntimeException("订单查询失败");
        }
    }

    @Override
    public void cancelOrder(IdentityInfoDTO infoDTO, String orderId) {
        if (StringUtils.isBlank(orderId)) {
            throw new RuntimeException("cnbe 订单号不能为空");
        }
        OrderCancelReq req = new OrderCancelReq();
        IdentityInfo info = new IdentityInfo();
        BeanUtil.copyProperties(infoDTO, info);
        req.setIdentityInfo(info);
        req.setOrderId(OrderCancelData.builder().orderId(orderId).build());
        req.setActionName("BookingCancel");
        ScrollingInfo scrollingInfo =
                ScrollingInfo.builder().displayReq(40).build();
        req.setScrollingInfo(scrollingInfo);
        String respData = null;
        try {
            respData = cnbeClient.callCNBE(buildParam(XMLUtil.convertObjectToXml(req)));
            OrderCancelResp resp = XMLUtil.convertXmlToObject(respData, OrderCancelResp.class);
            if (resp == null) {
                log.error("cancelOrder error respData {}", respData);
                throw new RuntimeException("booking error ");
            }

            if (resp.getMessageInfo().getCode() != 30000) {
                throw new RuntimeException(resp.getMessageInfo().getDescription());
            }
            if (!"31009".equals(resp.getData().getReturnCode())) {
                throw new RuntimeException(resp.getData().getReturnMessage());
            }
        } catch (Exception e) {
            log.error("cnbe cancelOrder error req {}, res : {}", XMLUtil.convertObjectToXml(req), respData, e);
            throw new RuntimeException("订单取消失败");
        }
    }


    private MultiValueMap<String, String> buildParam(String req) {
        MultiValueMap<String, String> postData = new LinkedMultiValueMap<>();
        postData.add("xmlRequest", req);
        return postData;
    }
}
